// $Id: move.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#include <iostream>
#include <boost/config.hpp> // for BOOST_HAS_NRVO
#include <boost/test/unit_test.hpp>
#include <xpcom/array.hpp>
#include "def.hpp"

namespace {

int default_count = 0;
int size_count = 0;
int copy_count = 0;
int move_count = 0;
int assign_count = 0;
int swap_count = 0;

void test_reset_counts()
{
    default_count = 0;
    size_count = 0;
    copy_count = 0;
    move_count = 0;
    assign_count = 0;
    swap_count = 0;
}

void test_print_counts()
{
    using namespace std;
    cout << "default_count: " << default_count << endl;
    cout << "size_count: " << size_count << endl;
    cout << "copy_count: " << copy_count << endl;
    cout << "move_count: " << move_count << endl;
    cout << "assign_count: " << assign_count << endl;
    cout << "swap_count: " << swap_count << endl;
    cout << endl;
}

class testarray : public xpcom::array_base<xpcom::array_traits<int> >
{
    typedef testarray this_type;
    typedef xpcom::array_base<xpcom::array_traits<int> > base_type;
public:
    testarray() :
        base_type()
    {
        ++default_count;
    }

    testarray(const testarray& rhs) :
        base_type(rhs)
    {
        ++copy_count;
    }

    testarray(xpcom::move_from<this_type> rhs) :
        base_type(xpcom::move_from<base_type>(rhs.source))
    {
        ++move_count;
    }

    testarray(size_type n) :
        base_type(n)
    {
        ++size_count;
    }

    ~testarray()
    {
    }

    testarray& operator=(testarray rhs)
    {
        ++assign_count;
        swap(rhs);
        return *this;
    }

    void swap(testarray& rhs)
    {
        ++swap_count;
        base_type::swap(rhs);
    }
};

// RVO
testarray test_return_array(bool b)
{
    if (b)
        return testarray(10);
    else
        return testarray(5);
}

// NRVO
testarray test_return_array2()
{
    testarray arr(10);
    for (int i = 0; i < 10; ++i) {
        arr[i] = i;
    }
    return arr;
}

}

BOOST_AUTO_TEST_CASE(test_move_construct)
{
    using namespace xpcom;
    typedef testarray Array;

    test_reset_counts();

    Array a1(4);
    Array a2 = a1; // copy
    BOOST_CHECK_EQUAL(a2.size(), 4);
    BOOST_CHECK_EQUAL(copy_count, 1);

    Array a3 = test_return_array(true); // move
    BOOST_CHECK_EQUAL(a3.size(), 10);
    BOOST_CHECK_EQUAL(copy_count, 1);

    Array a4 = test_return_array(false); // move
    BOOST_CHECK_EQUAL(a4.size(), 5);
    BOOST_CHECK_EQUAL(copy_count, 1);

#ifdef BOOST_HAS_NRVO
    Array a5 = test_return_array2(); // move
    BOOST_CHECK_EQUAL(a5.size(), 10);
    BOOST_CHECK_EQUAL(copy_count, 1);
#endif

    BOOST_CHECK_EQUAL(assign_count, 0);
    BOOST_CHECK_EQUAL(swap_count, 0);

    //test_print_counts();
}

BOOST_AUTO_TEST_CASE(test_move_assign)
{
    using namespace xpcom;
    typedef testarray Array;

    test_reset_counts();

    Array a1(4);
    Array a2;
    a2 = a1; // copy
    BOOST_CHECK_EQUAL(a2.size(), 4);
    BOOST_CHECK_EQUAL(copy_count, 1);
    BOOST_CHECK_EQUAL(assign_count, 1);
    BOOST_CHECK_EQUAL(swap_count, 1);

    Array a3;
    a3 = test_return_array(true); // move
    BOOST_CHECK_EQUAL(a3.size(), 10);
    BOOST_CHECK_EQUAL(copy_count, 1);
    BOOST_CHECK_EQUAL(assign_count, 2);
    BOOST_CHECK_EQUAL(swap_count, 2);

    Array a4(90);
    a4 = test_return_array(false); // move
    BOOST_CHECK_EQUAL(a4.size(), 5);
    BOOST_CHECK_EQUAL(copy_count, 1);
    BOOST_CHECK_EQUAL(assign_count, 3);
    BOOST_CHECK_EQUAL(swap_count, 3);

#ifdef BOOST_HAS_NRVO
    Array a5(2);
    a5 = test_return_array2(); // move
    BOOST_CHECK_EQUAL(a5.size(), 10);
    BOOST_CHECK_EQUAL(copy_count, 1);
    BOOST_CHECK_EQUAL(assign_count, 4);
    BOOST_CHECK_EQUAL(swap_count, 4);
#endif

    //test_print_counts();
}
